/**
 * @file JSONHandler.h
 * @brief Header file for the JSONHandler class, focusing on JSON data management.
 *
 * This class manages loading, saving, creating, and clearing JSON data using 
 * the nlohmann::json library. Data access is provided through specific accessor 
 * objects obtained via `nodeAccessor()` (recommended, type-safe, structured access 
 * via JSONNode) or `legacyAccessor()` (older, string-based access).
 *
 * @author liuzhiyu (liuzhiyu27@foxmail.com)
 * @copyright Copyright (c) 2023 by liuzhiyu, All Rights Reserved.
 */

#ifndef JSON_HANDLER_H
#define JSON_HANDLER_H

#include <string>
#include <vector>
#include <map>
#include <memory>
#include <mutex>
#include <functional>
#include <iostream>
#include <fstream>
#include <atomic>
#include <optional>
#include <filesystem>
#include <cstdint> // For uint64_t
#include <utility> // For std::pair, std::move

#include "json.hpp"
#include "JSONNode.h"         // Include the node accessor
#include "JsonFlatMapAccessor.h" // Include the flat map accessor
// JSONValueHandler.hpp might not be needed directly by JSONHandler anymore

#include <cstdint> // For uint64_t


// Forward declaration for the test function in main.cpp
// Ensure the signature matches exactly the one in main.cpp
class JSONHandler; // Forward declaration for the function signature
void testJsonHandlerFeatures(JSONHandler& handler, const std::string& originalFilePath);

/**
 * @class JSONHandler
 * @brief Manages JSON data lifecycle (load, save, create, clear) and provides accessors.
 *
 * This class is responsible for handling the core JSON data (`nlohmann::json`). 
 * To query or retrieve data, obtain an accessor object:
 * - `nodeAccessor()`: Returns a `JSONNode` for modern, type-safe, structured access (Recommended).
 * - `legacyAccessor()`: Returns a `JsonLegacyAccessor` for older, string-based access.
 *
 * Key features:
 * - Load and parse JSON from files or strings.
 * - Save JSON objects to files.
 * - Create default or custom configuration JSON files.
 * - Provide accessors for different data querying styles.
 * - Thread-safe operations for modification (`setJsonValue`) and state changes.
 *
 * Example Usage:
 *     JSONHandler handler;
 *     handler.loadFromJsonFile("config.json");
 *     // New style access (Recommended)
 *     int timeout = handler.nodeAccessor()["settings"]["timeout"].getIntOrDefault(-1);
 *     // Old style access
 *     std::string logLevel = handler.legacyAccessor().getValue("logLevel"); 
 *     handler.setJsonValue("newKey", "newValue"); // Modify data
 *     handler.saveToFile("updated_config.json");
 *
 * @note This class is not copyable or movable.
 */
class __attribute__((visibility("default"))) JSONHandler
{
public:
    // Define ModificationNotifier type alias
    using ModificationNotifier = std::function<void()>;

    // --- Constructors and Destructor ---
    /**
     * @brief Constructor that builds a JSON object from a file.
     * @param path Path to the JSON file.
     */
    JSONHandler(const std::string& path);

    /**
     * @brief Default constructor. Initializes random provider if C++17 features enabled.
     */
    JSONHandler();

    /**
     * @brief Default destructor.
     */
    ~JSONHandler() = default;

    // Delete copy and move constructors and assignment operators
    JSONHandler(const JSONHandler&) = delete;
    JSONHandler& operator=(const JSONHandler&) = delete;
    JSONHandler(JSONHandler&&) = delete;
    JSONHandler& operator=(JSONHandler&&) = delete;

    // --- Data Management ---
    /**
     * @brief Initializes the JSONHandler with a given source string (file path or JSON string).
     * @param source Pointer to the source string.
     * @return True if JSON is successfully loaded or parsed, false otherwise.
     */
    bool init(const std::string* source);

    /**
     * @brief Resets the JSON object by clearing and re-initializing from a source.
     * @param source Pointer to the source string (file path or JSON string).
     * @return True on successful reset, false otherwise.
     */
    bool reset(const std::string* source) noexcept;

    /**
     * @brief Clears the current JSON object and associated state (like main key).
     */
    void clear() noexcept;

    /**
     * @brief Loads a JSON object from a file.
     * @param path Path to the file.
     * @return True if loading is successful, false otherwise.
     */
    bool loadFromJsonFile(const std::string& path);

    /**
     * @brief Saves the internal JSON object to a file.
     * @param outputFileName The name of the file to save to.
     * @param dumpValue Formatting option for the JSON output (default is 4).
     * @return True if the file was successfully written, false otherwise.
     */
    bool saveToFile(const std::string& outputFileName, uint8_t dumpValue = 4);

    // --- Creation ---

    /**
     * @brief Creates and saves a JSON file with randomly generated content (C++17 only).
     * @param path The path where the file should be saved.
     * @param Max_Size_byte Maximum size of the generated JSON in bytes.
     * @param MaxDepth Maximum nesting depth of the generated JSON.
     * @note Requires the random provider to be initialized (done in default constructor).
     */
    void createRNGJsonFile(const std::string& path, std::size_t Max_Size_byte = 1024*1024*1, int MaxDepth = 10);

    /**
     * @brief Creates and saves a configuration file from a given nlohmann::json object.
     * @param path The path where the configuration file should be saved.
     * @param configJson The nlohmann::json object representing the configuration.
     * @return True if the configuration file is successfully saved, false otherwise.
     */
    bool createConfigJsonFile(const std::string& path, const nlohmann::json& configJson);

    /**
     * @brief Creates and saves a configuration file from a map where keys represent paths.
     * Keys in the map are treated as paths (e.g., "database.host") to create nested structures.
     * Uses '.' as the default path separator.
     * @param path The path where the configuration file should be saved.
     * @param configMap A map where keys are path strings and values are the corresponding string values.
     * @return True if the configuration file is successfully saved, false otherwise.
     */
    bool createConfigFromPathMap(const std::string& path, const std::map<std::string, std::string>& configMap);

    // --- Accessors ---
    /**
     * @brief Provides modern, type-safe, structured read-only access via JSONNode (Recommended).
     * @return A `JSONNode` object wrapping the root of the internal JSON data in a const context.
     */
    JSONNode nodeAccessor() const;

    /**
     * @brief Provides modern, type-safe, structured read/write access via JSONNode (Recommended).
     * @return A `JSONNode` object wrapping the root of the internal JSON data in a mutable context.
     * @note Modifications made through the returned node will mark the handler as dirty and increment the data version.
     */
    JSONNode nodeAccessor(); // Non-const overload for mutable access

    /**
     * @brief Provides access to JSON data as a flattened map<string, string> and allows setting by path.
     * @return A `JsonFlatMapAccessor` object for accessing and modifying the internal JSON data.
     * @warning Use only if the specific behavior (flattened string map, path-based set) is needed.
     *          Prefer `nodeAccessor()` for general use. Can incur overhead due to cache population.
     * @note This accessor can now modify the underlying JSON and set the dirty flag / increment version.
     */
    JsonFlatMapAccessor flatMapAccessor(); // Now non-const

    /**
     * @brief Gets the internal JSON object as a std::string.
     * @return A string representation of the JSON object.
     * @note This can be potentially expensive for large JSON objects.
     */
    std::string getJsonttoStdstring(int indent = -1) const;

    // --- Deep Key Existence Check ---
    /**
     * @brief Checks if the managed JSON object contains a specific key (deep recursive search).
     * @param key The key name to check for at any level.
     * @return True if the key exists anywhere in the JSON structure, false otherwise.
     * @note This directly searches the internal raw JSON structure, bypassing any caches.
     */
    bool containsKey(const std::string& key) const;

    // --- Other Info ---
    /**
     * @brief Gets the value of the main key (typically the first key found upon loading).
     * @return The string value of the main key, or empty if not set.
     */
    inline std::string getMainKey() const noexcept {
        // No lock needed for reading m_mainKey if writes are protected? 
        // Let's assume reads are frequent and writes rare/protected.
        return m_mainKey; 
    }

    /**
     * @brief Checks if the JSON data has been modified since the last load or save.
     * @return True if the data is dirty, false otherwise.
     */
    inline bool isDirty() const noexcept {
        return m_isDirty;
    }

private:
    // --- Private Helpers ---
     /**
     * @brief Sets the main key for the JSON object. Called internally on load/init.
     * @param key The string representing the key to be set as the main key.
     */
    void setMainKey(const std::string& key) noexcept {
        std::lock_guard<std::mutex> lock(m_keymutex); // Protect write
        m_mainKey = key;
    }

    /**
     * @brief Saves a given JSON object to a file (internal helper).
     * @param outputFileName Path to save.
     * @param dumpValue Formatting.
     * @param outputjson The JSON object to save.
     * @return True on success.
     */
    bool saveToFileImpl(const std::string& outputFileName, uint8_t dumpValue, const nlohmann::json &outputjson);

    /**
     * @brief Centralized function to call when data is modified.
     * Sets the dirty flag and increments the data version.
     * @note Consider thread safety if multiple threads can modify concurrently without external lock.
     */
    void notifyDataModified() {
        // If external locking is assumed, direct access is fine.
        // Otherwise, a mutex might be needed around these two operations.
        m_isDirty = true;
        ++m_dataVersion;
        // std::cout << "[Debug] Data Modified. Dirty: " << m_isDirty << ", Version: " << m_dataVersion << std::endl;
    }

    /**
     * @brief Internal helper to load JSON from a file without resetting version/dirty state.
     * @param inputFileName Path to load from.
     * @return True on success.
     */
    bool loadFromJsonFileInternal(const std::string& inputFileName);

    /**
     * @brief Recursively searches a JSON node for a specific key name.
     *
     * @param key The key name to search for.
     * @param currentJson The current JSON node to search within.
     * @return True if the key is found within currentJson or its descendants, false otherwise.
     */
    bool findKeyRecursive(const std::string& key, const nlohmann::json& currentJson) const;

    // --- Private Members ---
    nlohmann::json m_jsonObject; ///< The core JSON data object.
    std::string m_currentfile;  ///< Path of the file loaded, if applicable.
    std::string m_mainKey;      ///< The main key (often the root key).
    std::string m_currentKey;   ///< Potentially redundant now? Used in init/load. Keep for now.
    mutable std::mutex m_keymutex;     ///< Mutex primarily for protecting write access to m_mainKey, m_currentKey? And reads in const methods.
    bool m_isDirty{false}; ///< Flag indicating if m_jsonObject has been modified since load/save.
    uint64_t m_dataVersion{0}; ///< Version counter, increments on each modification.

    /**
     * @brief Gets the current data version number. (Moved to private)
     * This number increments each time the JSON data is modified.
     * Primarily used for internal cache invalidation.
     * @return The current data version.
     */
    inline uint64_t getDataVersion() const noexcept {
        return m_dataVersion;
    }

    // Internal helper functions for createRNGJsonFile
    std::optional<std::reference_wrapper<nlohmann::json>> getRandomObject(nlohmann::json& parentObject) const;
    std::optional<std::reference_wrapper<nlohmann::json>> getTargetLevel(nlohmann::json* rootKey, int targetDepth) const;
     template <typename T = std::pair<std::string, std::string>>
     inline bool setRandomObject(nlohmann::json& parentObject,std::optional<T>* outputPair = nullptr) const;
     

    // --- Friend Declarations ---
    friend class JsonFlatMapAccessor; // Allow accessor to use private members (like version)

};

#endif // JSON_HANDLER_H